package com.tzstcl.framework.interceptor;

import com.alibaba.fastjson.JSONObject;
import com.tzstcl.base.model.AjaxResult;
import com.tzstcl.base.model.BaseAPIModel;
import com.tzstcl.base.model.BaseModel;
import com.tzstcl.base.model.BaseWebModel;
import com.tzstcl.commons.encode.AesUtils;
import com.tzstcl.commons.utils.IpUtils;
import com.tzstcl.commons.utils.StringUtils;
import com.tzstcl.framework.redis.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Map;

/**
 * 公司：天筑科技股份有限公司
 * 作者：zgq
 * 日期：2019年01月03日
 * 说明：app拦截器，对接口参数进行验证
 */
@Slf4j
@Component
public class WebInterceptor implements HandlerInterceptor {

    @Value("${project.appKey}")
    private String AppKey;

    @Value("${project.appCheckFlag:true}")
    private Boolean AppCheckFlag;

    @Autowired
    private RedisService redisService;

    /**
     * 预处理回调方法，实现处理器的预处理（如检查登陆），第三个参数为响应的处理器，自定义Controller
     * 返回值：true表示继续流程（如调用下一个拦截器或处理器）；false表示流程中断（如登录检查失败），不会继续调用其他的拦截器或处理器，此时我们需要通过response来产生响应；
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Map<String, String[]> paramMap = request.getParameterMap();
        log.debug("请求参数：" + paramMap.toString());
        if (!AppCheckFlag) {//是否进行接口检查
            return true;
        }
        String ip = IpUtils.getIpAddr(request);
        log.info("api拦截成功：" + request.getRequestURI() + ",方式" + request.getMethod());
        String url = request.getRequestURI();
        String validMsg = "";
        BaseWebModel apiModel = new BaseWebModel();



        String[] plat = paramMap.get("plat");
        if (null != plat && plat.length > 0) {
            apiModel.setPlat(plat[0]);
            validMsg += "plat=" + plat[0] + "&";
        } else {
            log.debug("平台为空错误");
            erroReturn(response, "参数错误");
            return false;
        }
        String[] randomStr = paramMap.get("randomStr");
        if (null != randomStr && randomStr.length > 0) {
            apiModel.setRandomStr(randomStr[0]);
            validMsg += "randomStr=" + randomStr[0] + "&";
        } else {
            log.debug("随机码为空错误");
            erroReturn(response, "参数错误");
            return false;
        }
        String[] timestampObject = paramMap.get("timestamp");
        if (null != timestampObject && timestampObject.length > 0) {
            long timestamp = Long.valueOf(timestampObject[0]);
            apiModel.setTimestamp(timestamp);
            validMsg += "timestamp=" + timestamp + "&";
        } else {
            log.debug("时间戳为空错误");
            erroReturn(response, "参数错误");
            return false;
        }

        String[] token = paramMap.get("token");
        if (null != token && token.length > 0) {
            apiModel.setToken(token[0]);
            validMsg += "token=" + token[0] + "&";
        }

        String[] sign = paramMap.get("sign");
        if (null != sign && sign.length > 0) {
            apiModel.setSign(sign[0]);
        } else {
            log.debug("签名为空错误");
            erroReturn(response, "参数错误");
            return false;
        }
        //校验接口有效性
        // 1.timestamp  生产环境打开

        long now = System.currentTimeMillis();
        if (apiModel.getTimestamp() < (now - 1000 * 5 * 60) || apiModel.getTimestamp() > (now + 1000 * 5 * 60)) {//超时10分钟的调用不处理
            log.debug("接口超时错误");
            erroReturn(response, "接口超时了");
            return false;
        }

        //2.plat
        String[] platArray = {"Android", "iOS", "other"};
        if (!Arrays.asList(platArray).contains(apiModel.getPlat())) {
            log.debug("平台参数不在许可范围错误");
            erroReturn(response, "参数错误");
            return false;
        }
        //判断是否重复提交
        long count = redisService.incrBy(apiModel.getRandomStr(), 1);
        // 设置有效期
        if (count == 1) {
            redisService.expire(apiModel.getRandomStr(), 5);
        } else {
            log.debug("重复提交错误");
            erroReturn(response, "不能重复提交");
            return false;
        }
        //4.sign
        if (!verification(validMsg, apiModel.getSign())) {
            log.debug("签名未通过错误");
            erroReturn(response, "签名未通过");
            return false;
        }
        //记录日志
        return true;
    }

    private void erroReturn(HttpServletResponse response, String msg) throws IOException {
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        PrintWriter writer = response.getWriter();
        log.debug(msg);
        writer.println(JSONObject.toJSONString(AjaxResult.error(msg)));
        writer.close();
    }


    /**
     * 后处理回调方法，实现处理器的后处理（但在渲染视图之前），此时我们可以通过modelAndView（模型和视图对象）对模型数据进行处理或对视图进行处理，modelAndView也可能为null。
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {

    }

    /**
     * 整个请求处理完毕回调方法，即在视图渲染完毕时回调，如性能监控中我们可以在此记录结束时间并输出消耗时间，还可以进行一些资源清理，类似于try-catch-finally中的finally，但仅调用处理器执行链中
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {

    }

    /**
     * 验证接口sign是否正确
     *
     * @param validMsg 生成的需验证的字符串
     * @param sign     上传的加密串
     * @return
     */
    private boolean verification(String validMsg, String sign) {
//        log.debug("需要加密的字串："+validMsg);
        try {
            String encrypt = AesUtils.aesEncrypt(validMsg, AppKey);
            if (sign.equals(encrypt)) {
                return true;
            } else {
                return false;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 获取参数
     *
     * @param urlstr
     * @param name
     * @return
     */
    public String getParam(String urlstr, String name) {
        if (StringUtils.isBlank(urlstr) || StringUtils.isBlank(name)) {
            return null;
        }
        //有参数
        String[] params = urlstr.split("&");
        for (String param : params) {
            String[] keyValue = param.split("=");
            if (name.equals(keyValue[0].trim())) {
                return keyValue[1].trim();
            }
        }
        return null;
    }
}
